from .log import log
from .gui.screen import BaseScreen
from .gui.form.button import BackButton
from .gui.form.header import SimpleHeader
from .gui.form.relativelayout import HorizontallyFixedRelativeLayout
from .gui.form.textitem import TextFormItem
from .gui.form.dynamicposition import DynamicPosition
from .gui.form.dynamicposition import TopScreenEdge
from .gui.form.dynamicposition import RightScreenEdge
from .gui.form.dynamicposition import LeftScreenEdge
from .gui.form.button import ImageButton
from .gui.background import Image
from .gui.form.button import MultiStateButton
from .gui.form.dynamicposition import HBox
from .gui.form.button import BasicTextButton
from .gui.background import RectBackground
from .gui.form import Form
from .gui.background import SolidBackground
from .gui.form.dynamicposition import FormWidth
from .gui.app import GuiApp
import charger
import play
import autoplay
import gobject

class BasePlayScreen:
  def __init__(self, app, current_song):
    self._app = app
    self._play_screen_back_button = None
    self._play_button = None
    self._current_song = current_song
    # First timer updates once a minute and is normally around while playing.
    # Second timer updates once a second only while fast forwarding.
    self._timers = [None, None]
    self._paused_by_charger = False
    charger.AddCallback(self.OnChargerConnected, self.OnChargerDisconnected)

    self._text_attrs = {
        "fg_color": "#000000",
        "bg_color": None,
        "size": 20,
    }
    self._CreateUiElements()

  def _CreateUiElements(self):
    self._CreateScreen()
    self._CreateHeader()
    self._CreateLayout()
    self._CreateEpisodeInfo()
    self._CreatePlayButton()
    self._CreateFfRewindButtons()

  def _CreateScreen(self):
    """Create the BaseScreen."""
    self._play_screen = BaseScreen(self._app)

  def _CreateHeader(self):
    """Create the header line."""
    self._play_screen_back_button = BackButton(self._app, self._play_screen,
        'Back', 'episodes', pre_cb=self.StopPlaying)

    self._header = SimpleHeader(self._play_screen, "Playing",
        [self._play_screen_back_button])
    self._play_screen.Add(self._header)

  def _CreateLayout(self):
    """Initialize overall layout."""
    self._layout = HorizontallyFixedRelativeLayout(800)

  def _CreateEpisodeInfo(self):
    """Add text items that describe the episode to the layout."""
    self._podcast_name = TextFormItem(
        self._play_screen, "Podcast name", self._text_attrs)
    self._current_song.podcast_widget = self._podcast_name
    podcast_name_dp = DynamicPosition(self._podcast_name,
        hcenter=FormWidth(self._layout), below=TopScreenEdge(),
        vspacing=(10, 10))
    self._layout.AddItem(podcast_name_dp)

    self._song_name = TextFormItem(
        self._play_screen, "No name", self._text_attrs)
    self._current_song.widget = self._song_name

    self._song_name_dp = DynamicPosition(self._song_name,
        hcenter=FormWidth(self._layout), below=podcast_name_dp)
    self._layout.AddItem(self._song_name_dp)

  def _CreatePlayButton(self):
    """Add the great big play/pause button to the layout."""
    play_state_button = ImageButton(self._play_screen, 0, 0,
        Image('gui/assets/play/play.png'),
        Image('gui/assets/play/play_sel.png'),
        self.Play)

    pause_state_button = ImageButton(self._play_screen, 0, 0,
        Image('gui/assets/play/pause.png'),
        Image('gui/assets/play/pause_sel.png'),
        self.Pause)

    self._play_button = MultiStateButton(self._play_screen, 0, 0,
        {frozenset(["play"]): play_state_button,
         frozenset(["pause"]): pause_state_button},
        frozenset(["pause"]))

    self._play_button_dp = DynamicPosition(self._play_button,
        hcenter=FormWidth(self._layout), below=self._song_name_dp)
    self._layout.AddItem(self._play_button_dp)

  def _CreateFfRewindButtons(self):
    def Button(caption, callback):
      return BasicTextButton(
        self._play_screen,
        0,
        0,
        RectBackground('#FFFFFF', 'gui/assets/radius/radius_', '.png'),
        RectBackground('#FFFFFF', 'gui/assets/radius/radius_', '_sel.png'),
        caption,
        { "fg_color": "#000000", "size": 26 },
        { "fg_color": "#000000", "size": 26 },
        callback,
        left_margin=20,
        right_margin=20,
        top_margin=55,
        bottom_margin=60)

    self._ff_button = Button("Fast fwd", self.FastForward)
    self._ff_button.SetMomentary(True)
    ff_button_dp = DynamicPosition(self._ff_button, left_of=RightScreenEdge(),
        vcenter=self._play_button_dp)
    self._layout.AddItem(ff_button_dp)

    self._rew_button = Button("Rewind", self.Rewind)
    self._rew_button.SetMomentary(True)
    rew_button_dp = DynamicPosition(self._rew_button, right_of=LeftScreenEdge(),
        vcenter=self._play_button_dp)
    self._layout.AddItem(rew_button_dp)

  def CommitLayout(self, name):
    """Call this after __init__ and after all widgets are added to the
    layout."""
    self._current_song.layout = self._layout
    self._dialog = Form(0, self._header.GetHeight(), self._layout,
        SolidBackground('#FFFFFF'))
    self._play_screen.Add(self._dialog)
    self._app.AddScreen(name, self._play_screen)
    self._screen_name = name

  def screen_name(self):
    return self._screen_name

  def _AddButton(self, caption, callback, predecessor, hbox_dp):
    """Add a text button below the play/pause button. Returns the tuple
    (button, button dynamic position)"""
    button = BasicTextButton(
        self._play_screen,
        0,
        0,
        RectBackground('#FFFFFF', 'gui/assets/radius/radius_', '.png'),
        RectBackground('#FFFFFF', 'gui/assets/radius/radius_', '_sel.png'),
        caption,
        { "fg_color": "#000000", "size": 26 },
        { "fg_color": "#000000", "size": 26 },
        callback,
        left_margin=20,
        right_margin=20,
        top_margin=15,
        bottom_margin=20)

    if predecessor:
      button_dp = DynamicPosition(button, right_of=predecessor,
          vcenter=hbox_dp, hspacing=(0, 10))
    else:
      button_dp = DynamicPosition(button, left_at=hbox_dp,
          vcenter=hbox_dp, hspacing=(0, 10))
    self._layout.AddItem(button_dp)
    hbox_dp.AddChild(button_dp)

    return button, button_dp

  def SetBackButton(self, screen_name):
    """Set the screen that the back button takes you to.
    screen_name: A string giving the screen name."""
    log("*** SET BACK BUTTON TO", screen_name)
    self._play_screen_back_button.SetDestName(screen_name)

  def SetButtonDisplay(self, paused):
    """If paused is true then change the button to show the pause icon (||),
    else show the play icon (>)."""
    if paused:
      self._play_button.SetState(frozenset(["pause"]))
    else:
      self._play_button.SetState(frozenset(["play"]))

  def SetListenedDisplay(self, listened):
    log("Mark listened button:", self._mark_listened_button)
    if not listened:
      self._mark_listened_button.SetState(frozenset(["listened"]))
    else:
      self._mark_listened_button.SetState(frozenset(["unlistened"]))

  def StopPlaying(self):
    """Stop playing the current file. Does not affect its stats. This is
    called when you hit the back button."""
    self.StopUpdates(0)
    self.StopUpdates(1)
    self._paused_by_charger = False
    if play.Playing():
      play.Stop()
      self._current_song.episode.UnlockFile()

  def Pause(self):
    """Pause playing the current file."""
    log("PAUSE button hit - stopping the music.")
    self.SetButtonDisplay(False)
    play.Pause()
    self.StopUpdates(0)

  def Play(self):
    """Resume playing the current file."""
    log("PLAY button hit - resuming the music")
    self.SetButtonDisplay(True)
    play.Unpause()
    self.StartUpdates(0)
    self._paused_by_charger = False

  def FastForward(self):
    log("FAST FORWARD button hit")
    # TODO: Set a timeout and increase the rate
    if self._ff_button.down():
      log("FF: Set speed to 2")
      self.SetSpeed(2, 1)
    else:
      log("FF: Set speed to 1")
      self.SetSpeed(1, 0)

  def Rewind(self):
    log("REWIND button hit")
    if self._rew_button.down():
      self.SetSpeed(-2.75, -1)
    else:
      self.SetSpeed(1, 0)

  def PlayEpisode(self, feed, episode, on_finish, prev_screen, 
      on_screen_change=None):
    """Begin playing an episode.
    feed: The feed that the episode belongs to.
    episode: The episode to play.
    on_finish: Called when the episode has played to the end (not when the
      back button is pressed).
    prev_screen: String name of screen to return to when back button is
      pressed.
    on_screen_change is called when the play screen is visible if it was
      not already visible."""
    episode.LockFile()
    self.SetButtonDisplay(True)
    self._current_song.podcast_widget = self._podcast_name
    self._current_song.widget = self._song_name
    self._current_song.SetEpisode(feed, episode)
    # Re-layout the dialog
    self._layout.SetPositions()

    def OnFinish():
      # Called when the episode ends. Stop the update timer and run the
      # client's callback.
      self.StopPlaying()
      on_finish()

    play.PlayFile(episode.filename(), OnFinish)
    self._app.SetCurrentScreen(self.screen_name(), GuiApp.ENTER_FROM_RIGHT,
        on_screen_change)
    self.StartUpdates(0)
    self.SetBackButton(prev_screen)
    self.SetListenedDisplay(episode.listened());
    self._paused_by_charger = False

  def StartUpdates(self, index, rate=60):
    log("StartUpdates: Creating timer", index, "to run every",rate,"seconds")
    self._timers[index] = gobject.timeout_add(
        rate * 1000, self.CurryTimer(index))

  def StopUpdates(self, index):
    self._timers[index] = None

  def CurryTimer(self, index):
    def OnTimer():
      log("Position update timer index", index, "running, ",self._timers[index])
      if self._timers[index] is None:
        log("Canceling timer", index)
        return False
      try:
        self.UpdatePosition()
      finally:
        return True
    return OnTimer

  def UpdatePosition(self):
    pos, total = play.QueryPosition()
    log("Position, duration=",pos, total)
    truncname = self._current_song.name
    max_name_len = 30
    if len(truncname) > max_name_len:
      truncname = truncname[:max_name_len]
    caption = "%s: %dmin of %dmin" % (truncname, pos/60, total/60)
    log("Update timer: set caption to", caption)
    self._current_song.widget.SetCaption(caption)
    self._play_screen.Paint()

  def OnChargerDisconnected(self, **args):
    """Called when the charger is unplugged to pause the playing episode."""
    log("Charger disconnected.")
    if play.Playing() and not play.Paused():
      self._paused_by_charger = True
      log("  -- pause --")
      self.Pause()

  def OnChargerConnected(self, **args):
    """Called when the charger is connected to resume the playing episode."""
    log("Charger connected.")
    if play.Playing() and play.Paused() and self._paused_by_charger:
      log("  -- unpause --")
      self.Play()

  def SetSpeed(self, speed, accel):
    if play.Playing():
      log("SetSpeed: is playing")
      if speed == 1:
        log("SetSpeed: speed is 1")
        if self._timers[1]:
          log("SetSpeed: stopping timer.")
          self.StopUpdates(1)
      else:
        log("SetSpeed: speed is not 1, is", speed, "timers are:", self._timers)
        if not self._timers[1]:
          log("Calling startupdates on timer 1.")
          self.StartUpdates(1, 1)
      play.SetSpeed(speed, accel)

  def CreateMarkListenedButton(self, hbox_dp):
    mark_listened_button = BasicTextButton(
        self._play_screen,
        0,
        0,
        RectBackground('#FFFFFF', 'gui/assets/radius/radius_', '.png'),
        RectBackground('#FFFFFF', 'gui/assets/radius/radius_', '_sel.png'),
        "Mark Listened",
        { "fg_color": "#000000", "size": 26 },
        { "fg_color": "#000000", "size": 26 },
        self._MarkListened,
        left_margin=20,
        right_margin=20,
        top_margin=15,
        bottom_margin=20)
    mark_unlistened_button = BasicTextButton(
        self._play_screen,
        0,
        0,
        RectBackground('#FFFFFF', 'gui/assets/radius/radius_', '.png'),
        RectBackground('#FFFFFF', 'gui/assets/radius/radius_', '_sel.png'),
        "Mark Unlistened",
        { "fg_color": "#000000", "size": 26 },
        { "fg_color": "#000000", "size": 26 },
        self._MarkUnlistened,
        left_margin=20,
        right_margin=20,
        top_margin=15,
        bottom_margin=20)
    self._mark_listened_button = MultiStateButton(mark_listened_button, 0, 0,
        {frozenset(["listened"]): mark_listened_button,
         frozenset(["unlistened"]): mark_unlistened_button},
        frozenset(["listened"]))
    log("Created Mark listened button:", self._mark_listened_button)

  def _MarkListened(self):
    log("************* MARK LISTENED")
    self._current_song.episode.set_listened(True)
    log("calling set listened display")
    self.SetListenedDisplay(True)

  def _MarkUnlistened(self):
    log("************* MARK UNLISTENED")
    self._current_song.episode.set_listened(False)
    self.SetListenedDisplay(False)


class PlayScreen(BasePlayScreen):
  def __init__(self, app, current_song):
    BasePlayScreen.__init__(self, app, current_song)
    self._AddPlayScreenUiElements()
    self.CommitLayout("play_episode")

  def _AddPlayScreenUiElements(self):
    hbox_dp = HBox(hcenter=FormWidth(self._layout), below=self._play_button_dp,
        vspacing=(80, 0))
    self.CreateMarkListenedButton(hbox_dp)
    button_dp = DynamicPosition(self._mark_listened_button, left_at=hbox_dp,
        vcenter=hbox_dp, hspacing=(0, 10))
    self._layout.AddItem(button_dp)
    hbox_dp.AddChild(button_dp)

